import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.HashMap;
import java.util.Map.Entry;

/**
 * Class defining Fuzzy numbers
 * Possible methods: a check method ensuring 
 * 	all segments are properly "joined"? 
 * 
 * @author Phil
 *
 */
public class FuzzyNumber {

	private HashMap<Double,GeneralSegment> alphacuts;

	public FuzzyNumber(HashMap<Double,GeneralSegment> alphacuts){
		this.alphacuts = alphacuts;
	}
	
	public FuzzyNumber(GeneralSegment segments){
		alphacuts = new HashMap<Double,GeneralSegment>();
		double alpha = 0;
		GeneralSegment newCut = this.calculateAlphaCut(alpha,segments);
		while (alpha<=1){
			alphacuts.put(alpha, newCut);
			alpha = alpha + Calculator.resolution;
			newCut = this.calculateAlphaCut(alpha,segments);
		}
	}
	
	/**
	 * Recreates a fuzzy number out of the given alphacuts. Do note you need to supply an
	 * intermediary alphacut for each two "real" alphacuts
	 * 
	 * @param alphacuts a SortedMap. keys must be doubles ordered so that higher alphacuts are
	 * in the LOWEST keys
	 */
	/*public FuzzyNumber(SortedMap<Double,GeneralSegment> alphacuts){
		//To begin with, we replace all Points by MarkablePoints
		Iterator<GeneralSegment> initIt = alphacuts.values().iterator();
		while (initIt.hasNext()){
			GeneralSegment cut = initIt.next();
			Iterator<Segment> initIt2 = cut.iterator();
			while (initIt2.hasNext()){
				Segment seg = initIt2.next();
				seg.setP1(new MarkablePoint(seg.getP1()));
				seg.setP2(new MarkablePoint(seg.getP2()));
			}
		}
		
		//We will proceed as such: go through the segments of the cuts from top to bottom, ignoring 
		//all those whose points are marked. For every such segment, we will check the cuts below and mark
		//the points of whichever sections of them are "interior" to the shape of the number.
		//On the way down, we will also collect the segments completing the shape of the number.
		//Once finished, we will remove all marked segments and re-make a coherent shape out of
		//the alpha cut segments we have kept and those we have gathered.
		
		Iterator<Entry<Double,GeneralSegment>> alphaIt = alphacuts.entrySet().iterator();
		while (alphaIt.hasNext()){
			Entry<Double,GeneralSegment> entry = alphaIt.next();
			double currentAlpha = entry.getKey();
			GeneralSegment cut = entry.getValue();
			Iterator<Segment> cutIt = cut.iterator();
			while(cutIt.hasNext()){
				Segment topseg = cutIt.next();
				int topsegIndex=cut.indexOf(topseg);
				//if the segment's points are not marked
				if(!((MarkablePoint)topseg.getP1()).isMarked() && !((MarkablePoint)topseg.getP1()).isMarked()){
					//SortedMap containing all the alphacuts strictly below our level.
					SortedMap<Double,GeneralSegment> lowercuts = alphacuts.tailMap(currentAlpha);
					lowercuts.remove(currentAlpha);
					
					//we go left first.
					Iterator<Entry<Double,GeneralSegment>> leftAlphaIt = lowercuts.entrySet().iterator();
					//this is the lateral segment we'll be building up as we go
					Segment leftSegment = new Segment(topseg.getP1(),topseg.getP1());
					while(leftAlphaIt.hasNext()){
						Entry<Double,GeneralSegment> intercut = leftAlphaIt.next();
						double interAlpha = intercut.getKey();
						Segment interSegment = utilFindLowerSeg(topseg,intercut.getValue());
						
						Entry<Double,GeneralSegment> lowcut = leftAlphaIt.next();
						double lowAlpha = lowcut.getKey();
						Segment lowSegment = utilFindLowerSeg(topseg,lowcut.getValue());
						
						
					}
				}
				
			}	
		}	
	}
	
	private Segment utilFindLowerSeg(Segment upperseg, GeneralSegment lowersegs){
		Iterator<Segment> it = lowersegs.iterator();
		while(it.hasNext()){
			Segment seg = it.next();
			if (seg.getP1().getX()<=upperseg.getP1().getX() && seg.getP2().getX()>=upperseg.getP2().getX()){
				return seg;
			}
		}
		return null;
	}
	private  utilIntersect(Point top, Point inter, Segment low){
		
	}*/

	/**
	 * 
	 * @return a list of the alphas of all horizontal segments
	 */
	/*public ArrayList<Double> getAlphas(){
		Iterator<Segment> it = segments.iterator();
		ArrayList<Double> tab = new ArrayList<Double>(segments.size());
		//the returned array is much bigger 
		//than it needs to be, but what the hell
		int cpt = 0;
		while(it.hasNext()){
			Segment seg = it.next();
			if ((seg.getA()==0  
					||seg.getA()==Double.NEGATIVE_INFINITY) && !tab.contains(seg.getP1().getY())
					){
				tab.add(cpt, seg.getP1().getY());
			}
		}
		Collections.sort(tab);
		return tab;
	}*/

	private GeneralSegment calculateAlphaCut(double alpha, GeneralSegment segments){
		GeneralSegment alphaCut = new GeneralSegment();
		boolean segmentStartedFlag = false;
		Point lastPoint = null;
		Segment lastSegment = null;
		for(Segment seg : segments){
			Segment alphaSegment = new Segment(	new Point(seg.getP1().getX(),alpha), 
					new Point(seg.getP2().getX(), alpha));
			try {	
				Point pointCut = seg.pointIntersection(alphaSegment);
//				System.out.println("Segment "+seg);
//				System.out.println("Segment alpha "+alphaSegment);
//				System.out.println("Point "+pointCut);
				if(pointCut!=null){
					if(segmentStartedFlag){
						alphaCut.add(new Segment(lastPoint, pointCut));
						segmentStartedFlag = false;
					}else if(lastSegment!=null){
						alphaCut.remove(lastSegment);
						lastSegment = new Segment(lastSegment.getP2(),pointCut); 
						alphaCut.add(lastSegment);
						segmentStartedFlag = false;
					}else{
						lastPoint = pointCut;
						lastSegment = null;
						segmentStartedFlag = true;
					}
				}
			}catch (IntersectsAsSegmentException e) {
				try {
					Segment segmentCut = seg.segmentIntersection(alphaSegment);
					if (segmentCut != null) {
						if (segmentStartedFlag) {
							alphaCut.add(new Segment(lastPoint, segmentCut.getP2()));							
							lastSegment = segmentCut;
							segmentStartedFlag = false;
						} else if (lastSegment != null ) {
							alphaCut.remove(lastSegment);
							lastSegment = new Segment(lastSegment.getP1(),segmentCut.getP2());
							alphaCut.add(lastSegment);
							lastSegment = segmentCut;
							segmentStartedFlag = false;
						} else {
							alphaCut.add(segmentCut);
							segmentStartedFlag = false;
						}
					}
				} catch (IntersectsAsPointException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		}
		//We normalize the cut and return it
		//System.out.println("alpha cut"+alphaCut);
		return normalizeAlphaCut(alphaCut);
	}

	private GeneralSegment normalizeAlphaCut(GeneralSegment alphacut){
		Iterator<Segment> it = alphacut.iterator();
		GeneralSegment normalizedAC = new GeneralSegment();
		Segment lastSeg = null;
		Segment thisSeg;
		
		while (it.hasNext()){
			thisSeg = it.next();
			/*if(thisSeg != null){
				System.out.println("thisseg notnull");
			}*/
			if (lastSeg == null){
				//First step, never repeats
				lastSeg = thisSeg;
			}
			else {
				//If the new segment is connected to the old one, we fuse them together
				if (lastSeg.getP2().getX() == thisSeg.getP1().getX()
						&& lastSeg.getP2().getY() == thisSeg.getP1().getY()){
					lastSeg = new Segment(lastSeg.getP1(),thisSeg.getP2());
				}
				//Otherwise, we add the last segment to our new list and keep going
				else{
					normalizedAC.add(lastSeg);
					lastSeg = thisSeg;
				}
			}
		}
		//Finally, we add the last found segment, if it's not null
		if (lastSeg != null){
			normalizedAC.add(lastSeg);
		}
		return normalizedAC;
	}

	public HashMap<Double, GeneralSegment> getAlphacuts() {
		return alphacuts;
	}

	public void setAlphacuts(HashMap<Double, GeneralSegment> alphacuts) {
		this.alphacuts = alphacuts;
	}
}
